home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / prog / asm_n_z.arj / SWAPOUT.ASM < prev    next >
Assembly Source File  |  1989-07-16  |  44KB  |  1,087 lines

  1. ; SWAP - (c) Copyright 1988 Nico Mak and Mansfield Software Group
  2. ; All rights reserved
  3. ;
  4. ; To rebuild SWAP.COM use the following instructions:
  5. ;   masm swap;
  6. ;   link swap;
  7. ;   exe2bin swap swap.com
  8. ;
  9. cr              equ     13
  10. lf              equ     10
  11.  
  12. error   macro   message                 ;; macro to display an error message
  13.         local   around, msg, msglen     ;; and to jump to error_exit routine
  14.         jmp     around
  15. msg     db      &message,cr,lf          ;; define error message
  16. msglen  equ     $-msg
  17. around:
  18.         mov     dx,offset msg           ;; get address of error message
  19.         mov     cx,msglen               ;; get length
  20.         jmp     error_exit              ;; jump to error exit routine
  21.         endm
  22.  
  23. ; -------------------------------------------------------------------
  24. ; the following is copied over the swappee
  25. ; -------------------------------------------------------------------
  26. code    segment 'code'
  27.         assume  cs:code,ds:code
  28.         org     100h                    ; org past psp
  29. swap    proc    near
  30.         jmp     begin
  31.                 db      20 dup('STACK')
  32. stack           equ     $
  33.  
  34. flag            db      0       ; option flag
  35. flag_copy       equ     80h     ; copy stdout to 'con'
  36. flag_force      equ     40h     ; swap even if vector points to swappee
  37. flag_quiet      equ     20h     ; don't print hello message
  38. flag_disk       equ     10h     ; swap to disk
  39.  
  40. emsg_ems        db      "SWAP EMS Error "
  41. ems_rc          db      'xx function '
  42. ems_func        db      'xx',cr,lf
  43. emsg_ems_len    equ     $-emsg_ems
  44.  
  45. my_psp          dw      ?       ; segment of SWAP's original psp
  46. swappee_psp     dw      ?       ; segment of swappee's psp
  47.  
  48. ; variables used when swapping to expanded memory
  49. ems_handle      dw      ?       ; emm handle
  50. swap_pages      dw      ?       ; number of pages for ems_handle
  51. ems_frame       dw      ?       ; ems page frame
  52. last_ems_func   db      ?       ; last emm function issued by swap
  53.  
  54. ; variables used when swapping to disk
  55. swap_fid        db      "c:\swap.dat",0 ; asciiz string to open swap file
  56. swap_handle     dw      ?       ; handle while swap file is open
  57.  
  58. ; fields for int 21 function 4b (exec)
  59. commandcom_addr dd      ?       ; address of program to exec (command.com)
  60. exec_sp         dw      ?       ; save area for reg clobbered by exec function
  61. command_line    db      ?,"/c"          ; command line for command.com
  62. command_text    db      130 dup (0)     ; command line continued
  63. blank_fcb       db      36 dup (0)      ; dummy fcb for exec function
  64. exec_parm_block equ     $               ; exec parameter block
  65. exec_env        dw      ?               ; segment addr of environment
  66. cmdline_addr    dw      offset command_line     ; address of command line
  67. cmdline_seg     dw      ?
  68.                 dw      offset blank_fcb        ; address of fcb
  69. fcb1_seg        dw      ?
  70.                 dw      offset blank_fcb        ; address of fcb
  71. fcb2_seg        dw      ?
  72.  
  73. ; fields used by int 21 handler
  74. save_pid        dw      ?       ; pid at time int 21 handler received control
  75. int21_vector    dd      ?       ; original int 21 vector owner
  76. con             db      "con",0 ; asciiz string to open console
  77. handle          dw      ?       ; handle while "con" is open
  78. char_buf        db      ?       ; buffer for int 21 function 2 and 6 handlers
  79. save_ax         dw      ?       ; register save areas for int 21 handler
  80. save_bx         dw      ?
  81. save_cx         dw      ?
  82. save_dx         dw      ?
  83. save_ds         dw      ?
  84.  
  85. ; -------------------------------------------------------------------
  86. ; run_command - the following code is copied over the swappee
  87. ; -------------------------------------------------------------------
  88. run_command:
  89.         call    copy_start              ; start copying stdout to the console
  90.         call    exec_user_cmd           ; execute the user's command
  91.         call    copy_stop               ; stop copying stdout to the console
  92.         call    swap_in                 ; swap in all but first 16k
  93.         retf
  94.  
  95. ; -------------------------------------------------------------------
  96. ; subroutines for run_command follow
  97. ; -------------------------------------------------------------------
  98.  
  99. ; -----
  100. ; copy_start - if -c option specified, open handle for console and hook int 21
  101. ; -----
  102. copy_start:
  103.         test    flag,flag_copy          ; will we copy stdout to display?
  104.         jz      copy_start_ret          ; no
  105. ; ----- open a handle that points to "con"
  106.         mov     dx,offset con           ; address of asciiz file name
  107.         mov     ax,3d01h                ; code to open handle for writing
  108.         int     21h                     ; open the file
  109.         mov     handle,ax               ; remember handle
  110.         jnc     open_worked             ; did open succeed?
  111.         and     flag,255-flag_copy      ; no, then we won't copy stdout ...
  112.         jmp     short copy_start_ret    ; ... and won't hook int 21
  113. open_worked:
  114. ; ----- hook int 21 vector
  115.         mov     ax,3521h                ; code to get interrupt 21 vector
  116.         int     21h                     ; ask dos for address in vector
  117.         mov     word ptr int21_vector,bx; save offset
  118.         mov     word ptr int21_vector[2],es ; save segment
  119.         mov     dx,offset int21_handler ; address of our int 21 handler
  120.         mov     ax,2521h                ; code to set interrupt 21 address
  121.         int     21h                     ; tell dos to set int 21 vector
  122. ; ----- ensure that standard error is redirected and copied
  123.         mov     al,cs:[19h]             ; get stdout file handle array entry
  124.         mov     cs:[1ah],al             ; use stdout entry for stderr entry
  125. copy_start_ret:
  126.         ret
  127.  
  128. ; -----
  129. ; exec_user_cmd - set up and issue the int 21 function 4b (exec)
  130. ; -----
  131. exec_user_cmd:
  132.         mov     cs:exec_sp,sp           ; save register
  133.         mov     ax,cs:[2ch]             ; pass address of our environment
  134.         mov     exec_env,ax             ; to exec function
  135.         mov     word ptr cmdline_seg,ds ; address of command line
  136.         mov     word ptr fcb1_seg,ds    ; fill in segments for fcbs
  137.         mov     word ptr fcb2_seg,ds
  138.         push    cs
  139.         pop     es
  140.         mov     bx,offset exec_parm_block       ; bx = exec parameter block
  141.         lds     dx,commandcom_addr      ; es:bx = asciiz string of command.com
  142.         mov     ax,4b00h                ; code to load and execute a program
  143.         int     21h                     ; tell dos to execute the user's program
  144.         mov     ds,cs:swappee_psp       ; restore ds addressability
  145.         cli                             ; turn off interrupts
  146.         mov     ss,swappee_psp          ; restore stack
  147.         mov     sp,exec_sp
  148.         sti                             ; allow interrupts
  149.         ret
  150.  
  151. ; -----
  152. ; copy_stop - close handle and restore original int 21 vector
  153. ; -----
  154. copy_stop:
  155.         test    cs:flag,flag_copy       ; did we copy stdout to display?
  156.         jz      copy_stop_ret           ; no
  157. ; ----- close handle for console
  158.         mov     bx,handle               ; close handle for 'con'
  159.         mov     ah,3eh                  ; dos function = close handle
  160.         int     21h                     ; tell dos to close 'con'
  161. ; ----- restore original int 21 vector
  162.         push    ds                      ; ds gets clobbered, so save it
  163.         lds     dx,int21_vector         ; get address of old int 21 vector
  164.         mov     ax,2521h                ; code to set interrupt 21 address
  165.         int     21h                     ; tell dos to change it
  166.         pop     ds                      ; restore ds addressability
  167. copy_stop_ret:
  168.         ret
  169.  
  170. ; -----
  171. ; swap_in - swap in all but the first page of swappee
  172. ; -----
  173. swap_in:
  174.         mov     bx,cs                   ; bx = swappee's psp
  175.         add     bx,3ffh                 ; first page to swap in over
  176.         mov     es,bx
  177.         test    flag,flag_disk
  178.         jnz     swap_in_disk
  179. ; ----- swap in from expanded memory
  180.         mov     cx,1                    ; start with second logical page
  181.         cld
  182. swap_in_page:                           ; loop to swap 16K
  183.         mov     bx,cx                   ; logical page
  184.         call    map_page
  185.         push    ds                      ; save ds
  186.         mov     ds,ems_frame            ; ds = where to swap from
  187.         mov     si,0
  188.         mov     di,0
  189.         push    cx
  190.         mov     cx,4000h                ; copy 16K
  191.         rep     movsb
  192.         pop     cx
  193.         pop     ds                      ; restore ds
  194.         mov     bx,es
  195.         add     bx,400h
  196.         mov     es,bx                   ; es = next place to swap to
  197.         inc     cx
  198.         cmp     cx,swap_pages
  199.         jl      swap_in_page
  200.         ret
  201. ; ----- swap in from disk
  202. swap_in_disk:                           ; es = first page to swap over
  203.         call    open_swap_file          ; open the swap file
  204.         mov     cx,0                    ; high order part of offset
  205.         mov     dx,4000h                ; file pointer to start + 16k
  206.         mov     bx,swap_handle          ; get swap file handle
  207.         mov     ax,4201h                ; code to lseek from current location
  208.         int     21h                     ; tell dos to lseek to 2nd page
  209.         jnc     lseek_done
  210.         error   "LSEEK on swap file failed"
  211. lseek_done:
  212.         mov     cx,1                    ; start with second logical page
  213. swap_in_disk_page:                      ; loop to swap 16K
  214.         call    read_swap_file          ; read 16k from swap file
  215.         mov     bx,es
  216.         add     bx,400h
  217.         mov     es,bx                   ; es = next place to swap to
  218.         inc     cx
  219.         cmp     cx,swap_pages
  220.         jl      swap_in_disk_page
  221.         call    close_swap_file
  222.         ret
  223.  
  224. ; -------------------------------------------------------------------
  225. ; int_21_handler and its subroutines follow
  226. ; -------------------------------------------------------------------
  227.         assume  ds:nothing
  228. int21_handler:
  229. ; ----- decide whether we will front-end this int 21 function
  230.         cmp     ah,02h
  231.         je      func02
  232.         cmp     ah,06h
  233.         je      func06
  234.         cmp     ah,09h
  235.         je      func09
  236.         cmp     ah,40h
  237.         je      func40
  238. ; ----- call the original int 21 vector owner
  239. do_real_thing:
  240.         jmp     cs:int21_vector
  241.  
  242. ; -----
  243. ; handle int 21 function 9 (print dollar-sign delimited string)
  244. ; -----
  245. func09:
  246.         call    front_start
  247.         push    di
  248.         push    es
  249.         mov     di,dx
  250.         mov     es,save_ds              ; address of string at es:di
  251.         mov     al,'$'                  ; scan for $
  252.         mov     cx,-1                   ; max bytes to scan
  253.         cld                             ; scan in forward direction
  254.         repne   scasb                   ; find the $
  255.         sub     di,dx
  256.         mov     cx,di                   ; length to write
  257.         dec     cx                      ; don't write the $
  258.         pop     es
  259.         pop     di
  260.         mov     ds,save_ds              ; ds addressability is blown
  261.         call    write_to_con            ; write buffer to display
  262.         mov     ds,cs:swappee_psp       ; restore ds addressability
  263.         jmp     front_done
  264.  
  265. ; -----
  266. ; handle int 21 function 6 (direct console i/o)
  267. ; -----
  268. func06:
  269.         cmp     dl,0ffh                 ; get input characters?
  270.         je      do_real_thing           ; yes, then there is no output to copy
  271.  
  272. ; -----
  273. ; handle int 21 function 2 (display character in dl register)
  274. ; -----
  275. func02:
  276.         call    front_start
  277.         mov     char_buf,dl             ; put character to write in buffer
  278.         mov     dx,offset char_buf      ; get address of buffer
  279.         mov     cx,1                    ; get length
  280.         call    write_to_con            ; write buffer to display
  281.         jmp     front_done
  282.  
  283. ; -----
  284. ; handle int 21 function 40 (write to file handle)
  285. ; -----
  286. func40:
  287.         call    front_start
  288. ; ----- verify that file handle array entry for this handle == stdout entry
  289.         push    di
  290.         push    es
  291.         mov     bx,save_bx              ; get caller's handle
  292.         mov     es,save_pid             ; psp for process issuing int 21
  293.         les     di,es:34h               ; address of caller's file handle array
  294.         mov     ah,es:[di+1]            ; file handle array entry for stdout
  295.         cmp     ah,es:[di+bx]           ; does handle entry == stdout entry?
  296.         pop     es
  297.         pop     di
  298.         jne     func40_done             ; no, don't copy to console
  299. ; ----- call real int 21 handler with handle opened for 'con'
  300.         mov     ds,save_ds              ; ds addressability blown
  301.         call    write_to_con            ; write buffer to display
  302.         mov     ds,cs:swappee_psp       ; restore ds addressability
  303. func40_done:
  304.         jmp     front_done
  305.  
  306. ; -----
  307. ; front_start - start front-ending int 21
  308. ; -----
  309. front_start:
  310.         assume  ds:nothing
  311. ; ----- establish ds addressability and save registers
  312.         mov     save_ds,ds
  313.         mov     ds,cs:swappee_psp       ; establish ds addressability
  314.         assume  ds:code                 ; tell assembler
  315.         mov     save_ax,ax              ; save registers
  316.         mov     save_bx,bx
  317.         mov     save_cx,cx
  318.         mov     save_dx,dx
  319. ; ----- remember caller's pid
  320.         mov     ah,51h                  ; dos function = get pid
  321.         int     21h                     ; tell dos to get pid
  322.         mov     save_pid,bx             ; remember pid
  323. ; ----- set pid so our file handle array is used
  324.         mov     bx,cs                   ; pid = my cs register
  325.         mov     ah,50h                  ; dos function = set pid
  326.         int     21h                     ; tell dos to set pid
  327.         ret
  328.  
  329. ; -----
  330. ; write_to_con - call original int 21H handler to write buffer to display
  331. ; -----
  332. write_to_con:
  333.         assume  ds:nothing
  334.         mov     bx,cs:handle            ; handle opened for 'con'
  335.         mov     ah,40h                  ; dos function = write to handle
  336.         pushf
  337.         call    dword ptr cs:int21_vector       ; call dos
  338.         ret
  339.  
  340. ; -----
  341. ; front_done - almost done front-ending int 21
  342. ; -----
  343. front_done:
  344.         assume  ds:code
  345. ; ----- restore caller's pid
  346.         mov     bx,save_pid             ; get pid of process that issued int 21
  347.         mov     ah,50h                  ; dos function = set pid
  348.         int     21h                     ; set pid
  349. ; ----- restore registers & go jump to previous int 21 handler
  350.         mov     ax,save_ax
  351.         mov     bx,save_bx
  352.         mov     cx,save_cx
  353.         mov     dx,save_dx
  354.         mov     ds,save_ds              ; ds addressability blown
  355.         jmp     do_real_thing
  356.  
  357. ; -------------------------------------------------------------------
  358. ; the following routines are used by both parts of the program
  359. ; -------------------------------------------------------------------
  360.  
  361. ; -----
  362. ; emm - remember emm function in case of error and issue int 67
  363. ; -----
  364. emm:
  365.         mov     last_ems_func,ah
  366.         int     67h                     ; call expanded memory manager
  367.         or      ah,ah
  368.         ret
  369.  
  370. ; -----
  371. ; ems_error - handle ems errors
  372. ; -----
  373. ems_error:
  374.         mov     di,offset ems_rc
  375.         call    hex_to_ascii            ; make ems error code printable
  376.         mov     ah,last_ems_func
  377.         mov     di,offset ems_func
  378.         call    hex_to_ascii            ; make last ems function printable
  379.         mov     cx,emsg_ems_len
  380.         mov     dx,offset emsg_ems
  381.         jmp     error_exit              ; go display error message and exit
  382.  
  383. ; ------
  384. ; hex_to_ascii - convert ah register contents to ascii hexadecimal at ds:di
  385. ; ------
  386. hex_to_ascii:
  387.         mov     dl,ah
  388.         mov     cx,2
  389. hex_char:
  390.         push    cx
  391.         mov     cl,4
  392.         rol     dl,cl
  393.         mov     al,dl
  394.         and     al,00fh
  395.         daa
  396.         add     al,0f0h
  397.         adc     al,040h
  398.         mov     [di],al
  399.         inc     di
  400.         pop     cx
  401.         loop    hex_char
  402.         ret
  403.  
  404. ; -----
  405. ; error_exit - display error message and exit
  406. ; ds:dx point to error message, cx has the length
  407. ; -----
  408. error_exit:
  409.         push    cx
  410.         push    dx
  411.         mov     dx,offset emsg_start
  412.         mov     cx,emsg_start_len
  413.         mov     bx,2                    ; handle for stderr
  414.         mov     ah,40h                  ; dos function = handle write
  415.         int     21h                     ; output error message to stderr
  416.         pop     dx
  417.         pop     cx
  418.         mov     bx,2                    ; handle for stderr
  419.         mov     ah,40h                  ; dos function = handle write
  420.         int     21h                     ; output error message to stderr
  421.         jmp     return
  422.  
  423. ; -----
  424. ; routines to open, read from, and close the swap file
  425. ; -----
  426. open_swap_file:
  427.         mov     dx,offset swap_fid      ; address of fileid to open
  428.         mov     ax,3d00h                ; open file in read-only mode
  429.         int     21h
  430.         jnc     open_exit
  431.         error   "Could not open swap file"
  432. open_exit:
  433.         mov     swap_handle,ax
  434.         ret
  435.  
  436. ; read_swap_file - read 16K from swap file to address in es:0
  437. ; saves cx
  438. read_swap_file:
  439.         push    cx
  440.         mov     bx,swap_handle          ; get swap file handle
  441.         mov     cx,4000h                ; read 16k
  442.         mov     dx,0                    ; buffer offset
  443.         push    ds
  444.         push    es
  445.         pop     ds                      ; buffer segment
  446.         mov     ah,3fh                  ; dos function = handle read
  447.         int     21h
  448.         pop     ds
  449.         pop     cx
  450.         jnc     read_exit
  451.         error   "Error reading swap file"
  452. read_exit:
  453.         ret
  454.  
  455. close_swap_file:
  456.         mov     bx,swap_handle          ; get swap file handle
  457.         mov     ah,3eh                  ; dos function = close file
  458.         int     21h
  459.         ret
  460.  
  461. ; -----
  462. ; return - return to DOS
  463. ; -----
  464. return:
  465.         mov     ax,4c00h                ; dos function to terminate
  466.         int     21h                     ; back to dos
  467.  
  468. ; -----
  469. ; map_page - map EMS logical page in bx into physical page 0
  470. ; -----
  471. map_page:
  472.         mov     al,0                    ; physical page
  473.         mov     dx,ems_handle           ; ems handle
  474.         mov     ah,44h                  ; map handle page
  475.         call    emm
  476.         jz      map_page_exit
  477.         jmp     ems_error
  478. map_page_exit:
  479.         ret
  480.  
  481. lowend  equ     $                       ; end of code copied to lower memory
  482.  
  483. ; -------------------------------------------------------------------
  484. ; the following is *not* copied on top of the swappee
  485. ; -------------------------------------------------------------------
  486.  
  487. hello           db      cr, lf, "SWAP Version 1.0  Copyright (c) 1988 Nico Mak"
  488.                 db      " and Mansfield Software Group", cr, lf
  489. hello_len       equ     $-hello
  490. emsg_start      db      "SWAP Error: "
  491. emsg_start_len  equ     $-emsg_start
  492. run_addr        dw      offset run_command     ; offset of run_command
  493. run_seg         dw      ?                      ; segment of run_command
  494. swappee_mcb     dw      ?               ; segment of mcb for swappee psp
  495. swappee_end     dw      ?               ; segment of mcb after swappee
  496. my_mcb_size     dw      ?
  497. next_mcb        dw      ?               ; address of next mcb
  498. next_code       db      ?               ; M/Z code in next MCB
  499. next_owner      dw      ?               ; etc
  500. next_size       dw      ?               ;
  501. ems_device_name db      "EMMXXXX0",0    ; expanded memory manager signature
  502. comspec         db      'COMSPEC='      ; environment variable name
  503. comspec_len     equ     $-comspec
  504.  
  505. mcb_info        struc                   ; important memory control block info
  506. addr            dw      ?               ; address of mcb
  507. owner           dw      ?               ; psp of owner
  508. len             dw      ?               ; length of mcb
  509. mcb_info        ends
  510.  
  511. max_mcbs        equ     100
  512. mcbs            mcb_info <>
  513. mcb_length      equ     $-mcbs
  514.                 db      (max_mcbs-1)*mcb_length dup (?)
  515.  
  516. ; -------------------------------------------------------------------
  517. ; mainline code run from system prompt
  518. ; -------------------------------------------------------------------
  519. begin:
  520.         assume  ds:code,es:code
  521.         mov     sp,offset stack         ; set up new stack pointer
  522.         call    say_hello               ; print copyright message
  523.         call    process_cmdline         ; check options, set up 'exec' cmdline
  524.         call    check_dos_version       ; ensure we have dos 3.0 or later
  525.         call    find_comspec            ; find comspec= in environment
  526.         call    shrink_ourself          ; free unneeded memory
  527.         call    get_mcb_info            ; get relevant info about mcbs
  528.         call    check_mcbs              ; ensure mcbs are in expected order
  529.         call    vector_check            ; ensure swappee has not hooked vectors
  530.         call    figure_pages            ; determine how many ems pages we need
  531.         call    init_ems                ; ems initialization, allocation, etc
  532.         call    swap_out                ; swap out swappee, command.com, and us
  533.         call    muck_with_memory        ; copy swap over swappee & set up mcbs
  534.         mov     ss,swappee_psp          ; switch to stack in low memory
  535.         call    run_user_command        ; go call run_command rtn in low memory
  536.         mov     ss,my_psp               ; switch back to original stack
  537.         call    swap_first              ; swap in first 16K
  538.         call    clean_up                ; restore original environment
  539. exit:
  540.         jmp     return                  ; leave SWAP
  541.  
  542. ; -------------------------------------------------------------------
  543. ; subroutines for code that is not copied to low memory follow
  544. ; -------------------------------------------------------------------
  545.  
  546. ; -----
  547. ; process_cmdline - process options, set up command line for exec function
  548. ; -----
  549. process_cmdline:
  550.         mov     bx,80h
  551. option_check:
  552.         inc     bx
  553.         cmp     byte ptr [bx],cr        ; carriage return?
  554.         jne     option_check2           ; no
  555.         error   "No command to execute"
  556. option_check2:
  557.         cmp     byte ptr [bx],' '       ; blank?
  558.         je      option_check
  559.         cmp     byte ptr [bx],'/'       ; option signal?
  560.         je      got_option
  561.         cmp     byte ptr [bx],'-'       ; option signal?
  562.         jne     copy_command_line
  563. got_option:
  564.         mov     byte ptr [bx],' '       ; blank out character on command line
  565.         inc     bx                      ; point at option
  566.         mov     al,byte ptr [bx]        ; get option
  567.         mov     byte ptr [bx],' '       ; blank out character on command line
  568.         or      al,' '                  ; convert option to lower case
  569.         cmp     al,'c'                  ; option 'c'?
  570.         jne     check_option_q
  571.         or      flag,flag_copy
  572.         jmp     option_check
  573. check_option_q:
  574.         cmp     al,'q'                  ; option 'q'?
  575.         jne     check_option_f
  576.         or      flag,flag_quiet
  577.         jmp     option_check
  578. check_option_f:
  579.         cmp     al,'f'                  ; option 'f'?
  580.         jne     check_option_d
  581.         or      flag,flag_force
  582.         jmp     option_check
  583. check_option_d:
  584.         cmp     al,'d'                  ; option 'd'?
  585.         jne     bad_option
  586.         or      flag,flag_disk
  587.         jmp     option_check
  588. bad_option:
  589.         error   "Invalid option"
  590. ; ----- copy remainder of our command line to command line for command.com
  591. copy_command_line:
  592.         mov     cl,ds:[80h]             ; length of my command line
  593.         inc     cl                      ; add one for cr
  594.         mov     si,81h                  ; address of my command line
  595.         mov     di,offset command_text  ; address of where to put it
  596.         xor     ch,ch                   ; zero uninitialized part of count
  597.         cld                             ; scan in forward direction
  598.         rep     movsb                   ; copy command line
  599. ; set length of new command line
  600.         mov     cl,ds:[80h]             ; length of my command line
  601.         add     cl,2                    ; add 2 for "/c"
  602.         mov     command_line,cl         ; save new length
  603.         ret
  604.  
  605. ; -----
  606. ; say_hello - print hello message
  607. ; -----
  608. say_hello:
  609.         test    flag,flag_quiet         ; was -q option used?
  610.         jnz     say_hello_exit          ; yes, skip this
  611.         mov     dx,offset hello         ; get address of message
  612.         mov     cx,hello_len            ; get length of message
  613.         mov     bx,2                    ; handle for stderr
  614.         mov     ah,40h                  ; dos function = write to handle
  615.         int     21h                     ; write copyright message
  616. say_hello_exit:
  617.         ret
  618.  
  619. ; -----
  620. ; check_dos_version - be sure this is dos 3.0 or higher
  621. ; -----
  622. check_dos_version:
  623.         mov     ah,30h                  ; dos function = get version
  624.         int     21h                     ; get dos version
  625.         cmp     al,3                    ; ok?
  626.         jae     dos_version_ret
  627.         error   "DOS version must be 3.0 or higher"
  628. dos_version_ret:
  629.         ret
  630.  
  631. ; -----
  632. ; find_comspec - find fileid for exec function
  633. ; -----
  634. find_comspec:
  635.         mov     es,es:2ch               ; es = environment segment
  636.         xor     di,di                   ; point to start of env in es:di
  637.         cld                             ; scan in forward direction
  638. ; ----- loop thru environment strings one by one, beginning here
  639. find_string:
  640.         test    byte ptr es:[di],-1     ; end of environment?
  641.         jnz     check_string            ; nope, continue
  642.         error   "Could not find COMSPEC= in environment" ; very unlikely
  643. ; ----- compare current env string to 'COMSPEC='
  644. check_string:
  645.         mov     si,offset comspec       ; point to 'COMSPEC=' string
  646.         mov     bx,di                   ; save ptr to start of env string
  647.         mov     cx,comspec_len          ; length of 'COMSPEC='
  648.         repe    cmpsb                   ; compare
  649.         je      found_comspec           ; found it
  650.         mov     di,bx                   ; restore ptr to start of env string
  651.         xor     al,al                   ; scan for end of string
  652.         mov     cx,-1
  653.         repne   scasb
  654.         jmp     find_string             ; go back for next string
  655. ; ----- found COMSPEC=
  656. found_comspec:
  657.         mov     word ptr commandcom_addr[0],di  ; remember address of ...
  658.         mov     word ptr commandcom_addr[2],es  ; ... asciiz "command.com"
  659.         ret
  660.  
  661. ; -----
  662. ; shrink_ourself - release unneeded memory
  663. ; -----
  664. shrink_ourself:
  665.         push    cs
  666.         pop     es                      ; address of start of SWAP memory
  667.         mov     bx,offset endcode+15    ; address of end of SWAP code
  668.         mov     cl,4
  669.         shr     bx,cl                   ; convert to paragraphs
  670.         mov     ah,4ah                  ; dos function = SETBLOCK
  671.         int     21h                     ; shrink ourselves
  672.         ret
  673.  
  674. ; -----
  675. ; get_mcb_info - get relevant info from mcb chain
  676. ; -----
  677. get_mcb_info:
  678.         mov     my_psp,cs               ; remember address of our PSP
  679.         mov     ah,52h                  ; undocumented function
  680.         int     21h                     ; get base of memory chain
  681.         mov     es,es:[bx]-2            ; this is it
  682.         mov     bx,offset mcbs
  683.         mov     dx,0                    ; count of MCBs
  684. mem_loop:
  685.         mov     [bx].addr,es
  686.         mov     cx,word ptr es:1        ; owner of mcb
  687.         mov     [bx].owner,cx
  688.         mov     cx,word ptr es:3        ; length of mcb
  689.         mov     [bx].len,cx
  690.         inc     dx                      ; increment count of MCBs
  691.         cmp     dx,max_mcbs
  692.         jle     mem_loop1
  693.         error   "Over 100 Memory Control Blocks in system"
  694. mem_loop1:
  695.         cmp     byte ptr es:0,'Z'       ; last memory block?
  696.         jne     mem_next
  697.         error   "Could not find SWAP's PSP"
  698. mem_next:
  699.         mov     cx,es                   ; copy seg addr of mcb
  700.         inc     cx                      ; next paragraph
  701.         cmp     cx,my_psp               ; is this our psp?
  702.         je      found_our_psp           ; yes
  703.         add     cx,[bx].len             ; add length of this mcb
  704.         mov     es,cx                   ; this is next memory block
  705.         add     bx,mcb_length           ; where next mcb goes
  706.         jmp     mem_loop                ; proceed
  707. found_our_psp:                          ; have found our psp
  708.         mov     dx,[bx].len
  709.         mov     my_mcb_size,dx          ; remember length of our mcb
  710.         add     cx,[bx].len             ; add length of memory
  711.         mov     next_mcb,cx             ; this is next memory block
  712. ; ----- remember information about the next mcb
  713.         mov     es,cx
  714.         mov     dl,es:0
  715.         mov     next_code,dl
  716.         mov     dx,es:1
  717.         mov     next_owner,dx
  718.         mov     dx,es:3
  719.         mov     next_size,dx
  720.         ret
  721.  
  722. ; -----
  723. ; check_mcbs - ensure mcbs are in expected order
  724. ; verify that our parent is command.com, find swappee psp, etc.
  725. ; -----
  726. check_mcbs:
  727.         mov     cx,cs:16h               ; our parent's address
  728.         mov     es,cx
  729.         mov     ax,es:16h               ; and our grandparent's address
  730.         cmp     ax,cx                   ; better be equal
  731.         jne     unknown_parent
  732.         mov     ax,cs:10h               ; our ctrl-break handler
  733.         cmp     ax,cx                   ; better equal our parent's address
  734.         je      skip_our_env
  735. unknown_parent:
  736.         error   "SWAP not directly run from COMMAND.COM"
  737. ; ----- back up to find swappee's mcb.  bx still points at entry for our mcb
  738. skip_our_env:
  739.         mov     cx,cs
  740.         call    prev_mcb
  741.         cmp     [bx].owner,cx           ; is this mcb for our environment?
  742.         jne     skip_command
  743.         call    prev_mcb
  744. ; ----- back up over all mcb's owned by command.com (es == command.com psp)
  745. skip_command:
  746.         mov     cx,es                   ; address of command.com psp
  747.         cmp     [bx].owner,cx           ; is this mcb owned by command.com?
  748.         je      command_loop            ; yes
  749.         error   "COMMAND.COM must immediately precede SWAP in memory"
  750. command_loop:
  751.         mov     dx,[bx].addr            ; remember address of mcb in case
  752.         mov     swappee_end,dx          ;   it is the one above swappee
  753.         call    prev_mcb                ; back up one mcb
  754.         cmp     [bx].owner,cx           ; is this mcb owned by command.com?
  755.         je      command_loop            ; yes, skip it
  756. ; ----- assume we have one of swappee's mcbs
  757. ;       back up over all it's mcb's till we reach psp
  758.         mov     cx,[bx].owner           ; cx = swappee's psp
  759. find_swappee_psp:
  760.         mov     dx,[bx].addr            ; address of this mcb
  761.         inc     dx                      ; address of memory
  762.         cmp     dx,cx                   ; is this swappee's psp?
  763.         je      found_swappee_psp       ; yes
  764.         call    prev_mcb                ; check previous psp
  765.         cmp     [bx].owner,cx           ; still owned by swappee?
  766.         je      find_swappee_psp        ; yes continue
  767.         error   "Unexpected MCB while looking for PSP of swappee"
  768. ; ----- we've found swappee's psp - bx points at mcb entry for swappee
  769. found_swappee_psp:
  770.         mov     es,[bx].owner           ; es = swappee's psp
  771.         mov     swappee_psp,es          ; remember swappee's psp
  772.         cmp     word ptr es:2ch,0       ; swappee must have an environment
  773.         jne     check_mcbs_ret
  774.         error   "Swappee does not have an environment"
  775. check_mcbs_ret:
  776.         ret
  777.  
  778. ; -----
  779. ; unless the -f option was specified, check whether vectors point at swappee
  780. ; note: only interrupts 1-79h (inclusive) are checked
  781. ; -----
  782. vector_check:
  783.         test    flag,flag_force
  784.         jnz     vector_check_ret
  785.         mov     cx,0                    ; start at the beginning
  786. next_vector:
  787.         inc     cx                      ; next vector
  788.         cmp     cx,80h                  ; all done?
  789.         jae     vector_check_ret        ; yes, no vectors hooked
  790.         mov     ah,35h                  ; get vector function
  791.         mov     al,cl                   ; vector number
  792.         int     21h                     ; call dos to get vector address
  793.         mov     dx,es                   ; get segment addr
  794.         push    cx
  795.         mov     cl,4                    ; shift count
  796.         add     bx,15                   ; round up
  797.         shr     bx,cl                   ; divide offset by 16
  798.         pop     cx
  799.         add     dx,bx                   ; compute segment
  800.         cmp     swappee_psp,dx          ; compare to start of swappee
  801.         jae     next_vector             ; no problem, keep looking
  802.         cmp     dx,swappee_end          ; compare to end of swappee
  803.         jae     next_vector             ; no problem either
  804.         error   "Swappee has hooked an interrupt vector"
  805. vector_check_ret:
  806.         ret
  807.  
  808. ; -----
  809. ; figure_pages - figure how many 16K pages of EMS we need
  810. ; -----
  811. figure_pages:
  812.         mov     cx,swappee_psp
  813.         dec     cx                      ; cx = swappee's mcb
  814.         mov     swappee_mcb,cx          ; remember address of mcb
  815.         mov     dx,next_mcb             ; dx = mcb after swap.com
  816.         sub     dx,cx                   ; dx = difference in paragraphs
  817.         mov     cx,10
  818.         shr     dx,cl                   ; convert paragraphs to 16k pages
  819.         or      dx,dx
  820.         jnz     figure2
  821.         error   "Less than 16K to swap"
  822. figure2:
  823.         inc     dx
  824.         mov     swap_pages,dx
  825.         ret
  826.  
  827. ; -----
  828. ; init_ems - ensure ems is up to par, allocate pages, and save page map
  829. ; -----
  830. init_ems:
  831.         test    flag,flag_disk
  832.         jz      find_emm
  833.         jmp     init_ems_exit
  834. ; ----- determine whether ems is installed
  835. find_emm:
  836.         mov     ax,3567h                ; code to get int 67 handler address
  837.         int     21h                     ; get interrupt vector
  838.         mov     di,0ah                  ; offset to name string
  839.         mov     si,offset ems_device_name ; correct ems name
  840.         mov     cx,8                    ; length of name
  841.         cld                             ; scan in forward direction
  842.         repe    cmpsb                   ; do the compare
  843.         jz      test_status             ; ems not loaded
  844.         error   "Could not find Expanded Memory Manager"
  845. ; ----- test ems status
  846. test_status:
  847.         mov     ah,40h                  ; code to test status
  848.         call    emm
  849.         jz      check_ems_version
  850.         jmp     ems_error
  851. ; ----- ensure that we have ems version 3.2 or later
  852. check_ems_version:
  853.         mov     ah,46h                  ; get version
  854.         call    emm
  855.         jz      got_ems_version
  856.         jmp     ems_error
  857. got_ems_version:
  858.         cmp     al,32h
  859.         jnb     get_page_frame
  860.         error   "Expanded Memory Manager version must be 3.2 or higher"
  861. ; ----- get page frame address
  862. get_page_frame:
  863.         mov     ah,41h                  ; code to get page frame addr
  864.         call    emm
  865.         mov     ems_frame,bx            ; where ems memory starts
  866.         jz      alloc_pages
  867.         jmp     ems_error
  868. ; ----- allocate ems pages
  869. alloc_pages:
  870.         mov     ah,43h
  871.         mov     bx,swap_pages
  872.         call    emm
  873.         mov     ems_handle,dx
  874.         jz      save_page_map
  875.         error   "Not enough free expanded memory"
  876. ; ----- save ems page map
  877. save_page_map:
  878.         mov     ah,47h                  ; save page map
  879.         mov     dx,ems_handle
  880.         call    emm
  881.         jz      init_ems_exit
  882.         jmp     ems_error
  883. init_ems_exit:
  884.         ret
  885.  
  886. ; -----
  887. ; swap_out - swap out swappee, command.com, and ourself
  888. ; -----
  889. swap_out:
  890.         mov     es,swappee_mcb
  891.         test    flag,flag_disk          ; swap to disk?
  892.         jnz     swap_out_disk           ; yes
  893. ; ----- swap out to expanded memory
  894.         mov     cx,0
  895.         cld
  896. swap_out_page:                          ; loop to swap 16K
  897.         mov     bx,cx                   ; logical page = loop count
  898.         call    map_page
  899.         mov     bx,ems_frame
  900.         assume  ds:nothing
  901.         push    es
  902.         pop     ds                      ; ds = where to swap from
  903.         mov     es,bx                   ; es = ems_frame
  904.         mov     si,0
  905.         mov     di,0
  906.         push    cx
  907.         mov     cx,4000h                ; copy 16K
  908.         rep     movsb
  909.         pop     cx
  910.         mov     bx,ds                   ; where to swap from
  911.         add     bx,400h                 ; add 16K
  912.         mov     es,bx                   ; es = next place to swap from
  913.         push    cs
  914.         pop     ds
  915.         assume  ds:code
  916.         inc     cx
  917.         cmp     cx,swap_pages           ; done swapping?
  918.         jl      swap_out_page           ; no, swap the next page
  919.         ret
  920. ; ----- swap out to disk
  921. swap_out_disk:                          ; es = swappee's mcb
  922.         mov     cx,0                    ; attribute
  923.         mov     dx,offset swap_fid
  924.         mov     ah,3ch                  ; dos function = create a file
  925.         int     21h
  926.         jnc     create_done
  927.         error   "Could not create swap file"
  928. create_done:
  929.         mov     swap_handle,ax
  930.         mov     cx,0                    ; number of pages swapped
  931. swap_out_disk_page:                     ; loop to swap 16K
  932.         push    cx                      ; remember number pages swapped
  933.         mov     bx,swap_handle          ; handle to write to
  934.         mov     cx,04000h               ; write 16k
  935.         xor     dx,dx                   ; offset to write from
  936.         push    ds
  937.         push    es
  938.         pop     ds                      ; segment to write from
  939.         mov     ah,40h                  ; dos function = write to handle
  940.         int     21h
  941.         pop     ds
  942.         jnc     write_worked1
  943.         error   "Error writing to swap file"
  944. write_worked1:
  945.         mov     bx,es                   ; where to swap from
  946.         add     bx,400h                 ; add 16K
  947.         mov     es,bx                   ; es = next place to swap from
  948.         pop     cx                      ; remember number of pages swapped
  949.         inc     cx                      ; now we've swapped one more page
  950.         cmp     cx,swap_pages           ; done swapping?
  951.         jl      swap_out_disk_page      ; no, swap the next page
  952.         call    close_swap_file
  953.         ret
  954.  
  955. ; -----
  956. ; muck_with_memory - copy part of SWAP over swappee's psp, set up mcbs, etc
  957. ; -----
  958. muck_with_memory:
  959.         mov     es,swappee_psp
  960. ; ----- copy code over swappee's psp
  961.         cld                             ; copy in forward direction
  962.         mov     cx,offset lowend        ; length of code to copy
  963.         mov     si,100h                 ; start copying after psp
  964.         mov     di,100h                 ; where to copy
  965.         rep     movsb                   ; copy code over swappee's psp
  966. ; ----- copy our file handle array down to swappee's psp
  967.         mov     cx,20                   ; length of file handle table
  968.         mov     si,18h                  ; address of our file handle table
  969.         mov     di,18h                  ; where to put file handle table
  970.         rep     movsb                   ; copy file handle table to swappee psp
  971. ; ----- set the file handle array size and offset in swappee's psp
  972.         mov     word ptr es:32h,20      ; length of file handle table
  973.         mov     word ptr es:34h,18h     ; offset of file handle table
  974.         mov     word ptr es:36h,es      ; segment of file handle table
  975. ; ----- now fix up the swappee's mcb (still has an M)
  976.         mov     es,swappee_mcb          ; address of swappee's mcb
  977.         mov     dx,offset lowend+15     ; offset to end of SWAP code
  978.         mov     cx,4
  979.         shr     dx,cl                   ; convert to paragraphs
  980.         mov     word ptr es:3,dx        ; put result in swappee's mcb
  981. ; ----- find address of mcb for memory that was freed up
  982.         mov     bx,swappee_psp          ; address of swappee's psp
  983.         add     bx,dx                   ; add paragraphs in swappee's mcb
  984.         mov     es,bx                   ; this is where mcb for free mem goes
  985. ; ----- fill in new mcb
  986.         mov     dx,next_mcb             ; address of mcb after original swap
  987.         sub     dx,bx                   ; compute paragraphs of free space
  988.         add     dx,next_size            ; add paragraphs for next mcb
  989.         mov     word ptr es:3,dx        ; fill in size
  990.         mov     dl,next_code            ; get id from next mcb
  991.         mov     byte ptr es:0,dl        ; copy id (M or Z)
  992.         mov     word ptr es:1,0         ; mark block as free
  993.         ret
  994.  
  995. ; -----
  996. ; run_user_command - call run_command routine in low memory
  997. ; -----
  998. run_user_command:
  999. ; ----- put swappee segment address into pointer to run_command
  1000.         mov     bx,swappee_psp
  1001.         mov     word ptr run_seg,bx     ; segment of swappee psp
  1002. ; ----- set pid to address of swappee psp
  1003.         mov     ah,50h                  ; dos function = set pid
  1004.         int     21h                     ; set process id
  1005. ; ----- call run_command in low memory
  1006.         mov     ds,bx
  1007.         assume  ds:nothing
  1008.         call    dword ptr cs:run_addr   ; call run_command
  1009.         mov     ds,cs:my_psp
  1010.         assume  ds:code
  1011. ; ----- restore pid to SWAP's psp
  1012.         mov     bx,cs                   ; pid = my cs register
  1013.         mov     ah,50h                  ; code to set pid
  1014.         int     21h
  1015.         ret
  1016.  
  1017. ; -----
  1018. ; swap_first - swap in first page that was swapped out
  1019. ; -----
  1020. swap_first:
  1021.         mov     es,swappee_mcb
  1022.         test    flag,flag_disk          ; swapping in from disk?
  1023.         jnz     swap_first_disk         ; yes
  1024. ; ----- swap in from expanded memory
  1025.         mov     bx,0                    ; logical page = 0
  1026.         call    map_page
  1027.         push    ds                      ; save ds
  1028.         mov     ds,ems_frame            ; ds = where to swap from
  1029.         mov     si,0
  1030.         mov     di,0
  1031.         mov     cx,4000h                ; copy 16K
  1032.         cld
  1033.         rep     movsb
  1034.         pop     ds                      ; restore ds
  1035.         ret
  1036. ; ----- swap in from disk
  1037. swap_first_disk:
  1038.         call    open_swap_file
  1039.         call    read_swap_file
  1040.         call    close_swap_file
  1041.         ret
  1042.  
  1043. ; -----
  1044. ; clean_up - restore ems or delete swap file
  1045. ; -----
  1046. clean_up:
  1047.         test    flag,flag_disk
  1048.         jnz     clean_up_disk
  1049. ; ----- restore ems page map
  1050.         mov     ah,48h                  ; restore page map
  1051.         mov     dx,ems_handle
  1052.         call    emm
  1053.         jz      deallocate
  1054.         jmp     ems_error
  1055. ; ----- deallocate the ems pages
  1056. deallocate:
  1057.         mov     ah,45h                  ; deallocate pages
  1058.         mov     dx,ems_handle
  1059.         call    emm
  1060.         jz      clean_up_exit
  1061.         jmp     ems_error
  1062. ; ----- delete swap disk file
  1063. clean_up_disk:
  1064.         mov     dx,offset swap_fid      ; file handle for swap file
  1065.         mov     ah,41h                  ; code to delete a file
  1066.         int     21h
  1067. clean_up_exit:
  1068.         ret
  1069.  
  1070. ; -----
  1071. ; prev_mcb - back up one entry in table of MCBs
  1072. ; -----
  1073. prev_mcb:
  1074.         sub     bx,mcb_length
  1075.         cmp     bx,offset mcbs
  1076.         jae     prev_mcb_ret
  1077.         error   "Memory Control Blocks not in expected order"
  1078. prev_mcb_ret:
  1079.         ret
  1080.  
  1081. endcode equ   $
  1082.         align   16
  1083.         db      16 dup(0)               ; so that at least on mcb follows swap
  1084. swap    endp
  1085. code    ends
  1086.         end   swap
  1087.